#include <iostream>
#include <string>
#include <unistd.h>
#include <sstream>
#include <vector>
#include <mutex>
#include <string.h>
#include "jc_pty.h"
#include "mqtt_config.h"
#include "jc-pi.h"

using namespace std;
bool _pty_is_running = false;
mutex mux;
int _on_error_code = 0;
vector<string> _cmd_queue;
vector<string> _ext_queue;
int _unlock_at = 0;
#define UNLOCK_HERE() _unlock_at = __LINE__

void _redirect_from_pty(char *buf, int len)
{
    mqtt_out_raw(buf, len);
}

void _on_mqtt_connection_error(int error_code)
{
    if (!error_code)
        return;
    _on_error_code = error_code;
    cerr<< "mqtt connection error: " << error_code << endl;
    mux.unlock();
    UNLOCK_HERE();
}

void on_message(char *topic, int topic_len, char *msg, int msg_len)
{
    if (topic_len > 4 && strncmp(topic + topic_len - 4, "/cmd", 4) == 0)
    {
        if (!_pty_is_running)
        {
            mux.unlock();
            UNLOCK_HERE();

            // save the cmd
            _cmd_queue.push_back(string(msg, msg_len));
            return;
        }
        else if (check_subprocess())
        {
            close_pty();
            _pty_is_running = false;
            _cmd_queue.push_back(string(msg, msg_len));
            mux.unlock();
            UNLOCK_HERE();
        }
        else
            input_into_pty_no_newline(msg, msg_len);
        return;
    }
    if (topic_len >= 4 && strncmp(topic + topic_len - 4, "/ext", 4) == 0)
    {
        if (msg_len < 4)
            return;
#ifdef DEBUG
        cout << "ext:" << string(msg, msg_len) << endl;
#endif
        // exit
        if (strncmp(msg, "exit", 4) == 0)
        {
            if (_pty_is_running)
                close_pty();
            _pty_is_running = false;
            return;
        }
        if (!_pty_is_running)
        {
            mux.unlock();
            UNLOCK_HERE();
            _ext_queue.push_back(string(msg, msg_len));
            return;
        }
        // size rows cols
        if (strncmp(msg, "size", 4) == 0)
        {
            istringstream ist(msg + 4);
            unsigned short rows, cols;
            ist >> rows >> cols;
            set_pty_size(rows, cols);
            return;
        }
        // echo
        if (strncmp(msg, "echo", 4) == 0)
        {
            set_pty_echo(true);
            return;
        }
        // -echo
        if (strncmp(msg, "-echo", 5) == 0)
        {
            set_pty_echo(false);
            return;
        }

        return;
    }

    if (topic_len > 5 && strncmp(topic + topic_len - 5, "/ping", 5) == 0)
    {
        client_publish("pong", msg, msg_len);
        return;
    }
}

int main(int argn, char *argv[])
{
    string confg_file = "../mqtt.ini";
    if (argn > 1)
    {
        confg_file = argv[1];
    }
    int rc = 0;
    rc = read_config_slaver(confg_file);
    if (rc)
        exit(rc);
    extern string client_id;
    rc = start_loop();
    // transfer the topics used
    auto _topic = "lily/" + client_id + "/ext";
    set_on_message_callback(on_message);
    set_on_error_callback(_on_mqtt_connection_error);
    mux.lock();
    _on_error_code = 0;
    // hook the master process
    try
    {
        while (1)
        {
            mux.lock();
            if (_on_error_code)
                break;
            if (_pty_is_running)
            {
#ifdef DEBUG
                cout << "unlock at " << _unlock_at << endl;
#endif
                throw "pty is running";
            }
            _pty_is_running = true;
            start_pty(_redirect_from_pty);
            for (auto &ext : _ext_queue)
            {
                on_message((char *)"/ext", 4, (char *)(ext.c_str()), ext.length());
            }
            _ext_queue.clear();
            for (auto &cmd : _cmd_queue)
            {
                input_into_pty_no_newline(cmd.c_str(), cmd.length());
            }
            // clear the queue
            _cmd_queue.clear();

            sleep(1);
        }
    }
    catch (const std::exception &e)
    {
        cerr << e.what() << '\n';
    }
    catch (...)
    {
        cerr << "fatal error, ready to exit" << endl;
    }
    mux.unlock();
    stop_loop();
    close_pty();
    return 0;
}